package hu.ozeki.quitar;
 
import java.io.IOException;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public abstract class OzSmsClient extends Thread{

	private final int BUFFERSIZE = 4096;
	private final int KEEPALIVE = 30000;
	private String host;
	private int port;
	private SocketChannel ozChannel = null;
	private Selector ozSelect = null;
	private boolean active = true;
	private SocketChannel wChannel = null;
	private SocketChannel rChannel = null;
	private String head = "";
	private String tail = "";
	private String sStx = Character.toString('\002');
	private String sEtx = Character.toString('\003');
	private String username = "";
	private String password = "";
	private boolean loggedIn = false;
	private long lastMessage = 0L;
	private int fLastErrorCode = 0;
	private String fLastErrorMessage = "";

	
	public OzSmsClient(String host, int port) throws IOException, InterruptedException {
		this.host = host;
		this.port = port;
		this.ozChannel = SocketChannel.open();
		this.ozChannel.configureBlocking(false);
		this.ozChannel.connect(new InetSocketAddress(this.host, this.port));
		this.ozSelect = Selector.open();
		this.ozChannel.register(this.ozSelect, 9);
		this.lastMessage = System.currentTimeMillis();
		setDaemon(true);
		start();
		Thread.sleep(500L);
	}

	
	
	public void logout() throws InterruptedException {
		while (this.active) {
			if (this.tail.length() == 0) {
				Thread.sleep(5000L);
				this.active = false;
			}
			Thread.sleep(1000L);
		}
	}

	
	
	public void run(){
		try {
			int keyNum = 0;
			Iterator i;
			for (; (this.active) || (keyNum > 0); i.hasNext()) {
				if (System.currentTimeMillis() - this.lastMessage >= 30000L) keepAlive();
					this.ozSelect.select(500L);

					Set keys = this.ozSelect.selectedKeys();
					keyNum = keys.size();
					if (keyNum == 0) Thread.sleep(500L);
					i = keys.iterator();
					
					continue;
					
					SelectionKey key = (SelectionKey)i.next();
					i.remove();
					
					if (key.isConnectable()) {
						if (this.wChannel == null) {
							this.wChannel = ((SocketChannel)key.channel());
						}
						if (this.wChannel.isConnectionPending()) this.wChannel.finishConnect(); 
					}else if (key.isReadable()) {
						if (this.rChannel == null) {
							this.rChannel = ((SocketChannel)key.channel());
						}

						ByteBuffer buffer = ByteBuffer.allocate(4096);
						this.rChannel.read(buffer);
						byte[] bytearr = new byte[4096 - buffer.remaining()];
						buffer.rewind();
						buffer.get(bytearr);
						parse(new String(bytearr));
					}
			}	//END FOR
		}catch (IOException e) {
			System.err.println(e.toString());
			e.printStackTrace();
		} catch (InterruptedException e) {
			System.err.println(e.toString());
			e.printStackTrace();
		} catch (Exception e) {
			System.err.println(e.toString());
			e.printStackTrace();
		} 
	}	//END RUN

	private void parse(String message) throws UnsupportedEncodingException, ParseException, InterruptedException {
		this.head = (this.tail + message);
		
		if (this.head.lastIndexOf(this.sEtx) == -1) {
			this.tail = this.head;
			this.head = "";
		} else if (this.head.lastIndexOf(this.sEtx) == this.head.length() - 1) {
			this.tail = "";
		}	else {
				this.tail = this.head.substring(this.head.lastIndexOf(this.sStx));
				this.head = this.head.substring(0, this.head.lastIndexOf(this.sEtx) + 1);
		}

		if (this.head.lastIndexOf(this.sEtx) != -1) {
			String[] tomb = this.head.split(this.sStx);
			for (String item : tomb)
				if (item.length() > 0) {
					Map map = OzSmsUtil.getQueryMap(item.trim());
					selectEvent(map);
				}
		}
	}

	private void selectEvent(Map<String, String> map) throws ParseException, InterruptedException{
		OzSMSMessage sms = new OzSMSMessage();
		DateFormat formatter = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
		if ((map.containsKey("action")) && (((String)map.get("action")).equals("error"))) {
			if ((map.containsKey("errormessage")) && (map.containsKey("errorcode")))
				doOnClientConnectionError(Integer.parseInt((String)map.get("errorcode")), (String)map.get("errormessage"));
			else
				doOnClientConnectionError(8010, "");
		} else if ((map.containsKey("action")) && (((String)map.get("action")).equals("sendmessage"))) {
			sms = OzSmsUtil.smsFromPdu(map, "acceptreport");
			if (sms.errorCode > 0)
			{
				doOnMessageDeliveryError(sms);
			}
			else
			{
				doOnMessageAcceptedForDelivery(sms);
			}
		} else if ((map.containsKey("action")) && (((String)map.get("action")).equals("deliveryreport"))) {
			String transaction = "";
			if (map.containsKey("transactionid")) {
				transaction = (String)map.get("transactionid");
			}
			String ackPDU = this.sStx + 
				"transactionid=" + 
				transaction + 
				"&action=deliveryreport" + 
				"&acceptreport.statuscode=0" + 
				"&acceptreport.statusmessage=acknowledged" + 
				this.sEtx;
			
			if (!sendPDU(ackPDU)){
				doOnClientConnectionError(this.fLastErrorCode, this.fLastErrorMessage);
			}else{
				String deliverystatus = "";
				sms = OzSmsUtil.smsFromPdu(map, "deliveryreport");
				if (map.containsKey("deliveryreport.deliveredtonetworkdate"))
					sms.sentDate = formatter.parse((String)map.get("deliveryreport.deliveredtonetworkdate"));
				if (map.containsKey("deliveryreport.deliveredtohandsetdate"))
					sms.receivedDate = formatter.parse((String)map.get("deliveryreport.deliveredtohandsetdate"));
				if (map.containsKey("deliveryreport.statuscode")) {
					deliverystatus = (String)map.get("deliveryreport.statuscode");
				}
				if (deliverystatus.equals("0")){
					doOnMessageDeliveredToNetwork(sms);
				}else if (deliverystatus.equals("1")){
					doOnMessageDeliveredToHandset(sms);
				}else{
					doOnMessageDeliveryError(sms);
				}
			}
		}else{
			String ackPDU;
			if ((map.containsKey("action")) && (((String)map.get("action")).equals("messagereceived"))){
				String transaction = "";
				if (map.containsKey("transactionid")) {
					map.get("transactionid");
				}

				ackPDU = this.sStx + 
					"transactionid=" + 
					transaction + 
					"&action=messagereceived" + 
					"&acceptreport.statuscode=0" + 
					"&acceptreport.statusmessage=acknowledged" + 
					this.sEtx;
				
				if (!sendPDU(ackPDU)){
					doOnClientConnectionError(this.fLastErrorCode, this.fLastErrorMessage);
				}else {
					try{
						sms = OzSmsUtil.smsFromPdu(map, "message");
						if (map.containsKey("deliveryreport.deliveredtonetworkdate"))
							sms.sentDate = formatter.parse((String)map.get("deliveryreport.deliveredtonetworkdate"));
						else if (map.containsKey("deliveryreport.deliveredtohandsetdate"))
							sms.receivedDate = formatter.parse((String)map.get("deliveryreport.deliveredtohandsetdate"));
						else if (map.containsKey("message.receiveddate"))
							sms.receivedDate = formatter.parse((String)map.get("message.receiveddate"));
							doOnMessageReceived(sms);
					}catch (Exception localException){
	
					}
				}
			}else if ((!map.containsKey("action")) || (!((String)map.get("action")).equals("keepalive"))){
				if ((map.containsKey("action")) && (((String)map.get("action")).equals("login"))) {
					if (((String)map.get("statuscode")).equals("0")) {
						this.loggedIn = true;
					}else {
						this.loggedIn = false;
					}
				} else if ((!map.containsKey("action")) || (!((String)map.get("action")).equals("getversion"))){
					System.out.println(map.toString());
					for (String s : map.keySet())
						System.out.println("|" + s + "| = |" + (String)map.get(s) + "|"); 
				}
			}
		}
	}
 
	public boolean login(String username, String password) throws UnsupportedEncodingException, InterruptedException { 
		this.username = username;
		this.password = password;
 
		if (!this.ozChannel.isConnected()) return false;
 
		String versioncheckpdu = this.sStx + 
			"transactionid=" + OzSmsUtil.getNextTransID() + "&" + 
			"action=getversion" + 
			"&responseformat=urlencoded" + 
			this.sEtx;
 
		String loginpdu = 
			this.sStx + 
			"transactionid=" + OzSmsUtil.getNextTransID() + "&" + 
			"action=login" + 
			"&username=" + URLEncoder.encode(this.username, "UTF-8") + 
			"&password=" + URLEncoder.encode(this.password, "UTF-8") + 
			"&responseformat=urlencoded" + this.sEtx;
 
		if (!sendPDU(versioncheckpdu)) {
			doOnClientConnectionError(this.fLastErrorCode, this.fLastErrorMessage);
		}
		if (!sendPDU(loginpdu)) {
			doOnClientConnectionError(this.fLastErrorCode, this.fLastErrorMessage);
		}
 
		for (int i = 0; (i < 10) && (!isLoggedIn()); i++) {
			Thread.sleep(100L);
		}
	
		return isLoggedIn(); 
	}
 
	private boolean sendPDU(String pdu) {
		try {
			if (this.ozChannel.isConnected()) {
				ByteBuffer buffer = null;
				buffer = ByteBuffer.wrap(new String(pdu).getBytes());
				if (this.wChannel != null) {
					while (buffer.hasRemaining())
						this.wChannel.write(buffer);
					this.lastMessage = System.currentTimeMillis();
				} else {
					return false;
				}buffer.clear();
				return true;
			}
				return false;
		}catch (Exception e) {
			this.fLastErrorCode = 8001;
			this.fLastErrorMessage = e.getMessage();
			
			try {
				logout();
			} catch (Exception localException1) {
			}
		}
		
		return false;
	}
 
	
	public String sendMessage(String sender, String receiver, String message, String messagetype, String serviceProvider) throws UnsupportedEncodingException {
		String messageId = OzSmsUtil.makeMessageID();
		int transId = OzSmsUtil.getNextTransID();
 
		String pdu = this.sStx + 
			"transactionid=" + transId + 
			"&action=sendmessage" + 
			"&responseformat=urlencoded" + 
			"&originator=" + URLEncoder.encode(sender, "UTF-8") + 
			"&recipient=" + URLEncoder.encode(receiver, "UTF-8") + 
			"&messagetype=" + URLEncoder.encode(messagetype, "UTF-8") + 
			"&messagedata=" + URLEncoder.encode(message, "UTF-8") + 
			"&messageid=" + URLEncoder.encode(messageId, "UTF-8") + 
			"&serviceprovider=" + URLEncoder.encode(serviceProvider, "UTF-8") + 
			this.sEtx;
 
		if (!sendPDU(pdu)) {
			OzSMSMessage sms = new OzSMSMessage();
			sms.messageId = messageId;
			sms.sender = sender;
			sms.receiver = receiver;
			sms.messageData = message;
			sms.messageType = messagetype;
			sms.serviceProvider = serviceProvider;
			sms.errorCode = 8001;
			sms.errorMessage = "Client disconnected from SMS gateway during message submission.";
		}
 
		return messageId;
	}
 
	public String sendMessage(String receiver, String message) throws UnsupportedEncodingException {
		return sendMessage("", receiver, message, "SMS:TEXT", "");
	}
 
	public String sendMessage(String sender, String receiver, String message) throws UnsupportedEncodingException {
		return sendMessage(sender, receiver, message, "SMS:TEXT", "");
	}
 
	public String sendMessage(String sender, String receiver, String message, String messagetype) throws UnsupportedEncodingException {
		return sendMessage(sender, receiver, message, messagetype, "");
	}
 
	private void keepAlive() {
		String keepAlivePdu = this.sStx + "transactionid=" + OzSmsUtil.getNextTransID() + "&action=keepalive&responseformat=urlencoded" + this.sEtx;
		if (!sendPDU(keepAlivePdu)) {
			this.fLastErrorCode = 8022;
			this.fLastErrorMessage = "Keepalive failed.";
			doOnClientConnectionError(this.fLastErrorCode, this.fLastErrorMessage);
		}
	}
 
	public boolean isLoggedIn() {
		return this.loggedIn;
	}
 
	public abstract void doOnMessageReceived(OzSMSMessage paramOzSMSMessage);
 
	public abstract void doOnMessageDeliveredToHandset(OzSMSMessage paramOzSMSMessage);
 
	public abstract void doOnMessageDeliveredToNetwork(OzSMSMessage paramOzSMSMessage);
 
	public abstract void doOnMessageAcceptedForDelivery(OzSMSMessage paramOzSMSMessage);
 
	public abstract void doOnMessageDeliveryError(OzSMSMessage paramOzSMSMessage);
 
	public abstract void doOnClientConnectionError(int paramInt, String paramString);
 
}

/* Location:           C:\Users\vGarnica\Documents\docOffice2013\java\OzekiSmsClient.jar
 * Qualified Name:     hu.ozeki.OzSmsClient
 * JD-Core Version:    0.6.2
 */